/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "hitrace/trace.h"
#include "file_cache_interceptor_default.h"
#include <filesystem>

namespace ImageKnifePro {

// static
std::shared_ptr<FileCacheInterceptorDefault> FileCacheInterceptorDefault::GetInstance()
{
    static auto fileInterceptor = std::make_shared<FileCacheInterceptorDefault>();
    return fileInterceptor;
}

FileCacheInterceptorDefault::FileCacheInterceptorDefault()
{
    name = "Default FileCacheInterceptor";
}

FileCacheInterceptorDefault::~FileCacheInterceptorDefault()
{
    for (auto &item : fileCacheMap_) {
        delete item.second;
    }
    fileCacheMap_.clear();
}

FileCache *FileCacheInterceptorDefault::GetFileCacheByName(const std::string &cacheName)
{
    if (cacheName.empty()) {
        return &fileCache_;
    }
    if (fileCacheMap_.find(cacheName) != fileCacheMap_.end()) {
        return fileCacheMap_[cacheName];
    }
    return nullptr;
}

bool CheckRealPath(std::string &filePath)
{
    // 转成real path
    std::error_code error_code;
    filePath = std::filesystem::weakly_canonical(filePath, error_code);
    if (error_code) {
        IMAGE_KNIFE_LOG(LOG_ERROR, "File Path：%{public}s Invalid: %{public}s",
                        filePath.c_str(), error_code.message().c_str());
        return false;
    }
    if (filePath.empty()) {
        return false;
    } else if (filePath[filePath.size() - 1] != '/') {
        // realPath有些不是以分割符结尾，统一加上避免影响比较
        filePath += '/';
    }

    // 创建目录检查是否合法
    std::filesystem::create_directories(filePath, error_code);
    if (error_code) {
        IMAGE_KNIFE_LOG(LOG_ERROR, "File Path：%{public}s Invalid: %{public}s",
                        filePath.c_str(), error_code.message().c_str());
        return false;
    }
    return true;
}

void FileCacheInterceptorDefault::SetBigEndFilePath(std::string filePath)
{
    if (CheckRealPath(filePath)) {
        fileCache_.filePath = filePath;
    }
}

CacheErrorCode FileCacheInterceptorDefault::AddSmallEndFileCache(std::string cacheName, std::string filePath)
{
    if (cacheName.empty()) {
        return CacheErrorCode::CACHE_NAME_EMPTY;
    }

    if (!CheckRealPath(filePath)) {
        return CacheErrorCode::FILE_PATH_INVALID;
    }
    // 检查是否与大端路径冲突
    if (fileCache_.filePath == filePath) {
        return CacheErrorCode::FILE_PATH_OCCUPIED;
    }

    for (auto &item : fileCacheMap_) {
        if (item.first == cacheName) {
            return CacheErrorCode::CACHE_NAME_OCCUPIED;
        }
        if (item.second->filePath == filePath) {
            return CacheErrorCode::FILE_PATH_OCCUPIED;
        }
    }

    FileCache *fileCache = new FileCache();
    fileCache->filePath = filePath;
    fileCacheMap_[cacheName] = fileCache;
    return CacheErrorCode::SUCCESS;
}

void FileCacheInterceptorDefault::Init(std::string cacheName)
{
    if (!cacheName.empty()) {
        // 初始化小端
        FileCache *fileCache = GetFileCacheByName(cacheName);
        if (fileCache != nullptr) {
            fileCache->InitFileCache();
        }
    } else if (!fileCache_.filePath.empty()) {
        fileCache_.InitFileCache();
    }
}

bool FileCacheInterceptorDefault::IsInitFinished(std::string cacheName)
{
    FileCache *fileCache = GetFileCacheByName(cacheName);
    if (fileCache != nullptr) {
        return fileCache->IsFileInitComplete();
    }

    return false;
}

int64_t FileCacheInterceptorDefault::GetCurrentCacheNum(std::string cacheName)
{
    FileCache *fileCache = GetFileCacheByName(cacheName);
    if (fileCache != nullptr) {
        if (fileCache->IsFileInitComplete()) {
            return fileCache->Size();
        } else {
            IMAGE_KNIFE_LOG(LOG_ERROR, "File Cache %{public}s Init Not Finish", cacheName.c_str());
        }
    } else {
        IMAGE_KNIFE_LOG(LOG_ERROR, "File Cache %{public}s Not Found", cacheName.c_str());
    }

    return -1;
}

int64_t FileCacheInterceptorDefault::GetCurrentCacheSize(std::string cacheName)
{
    FileCache *fileCache = GetFileCacheByName(cacheName);
    if (fileCache != nullptr) {
        if (fileCache->IsFileInitComplete()) {
            return fileCache->GetCurrentMemory();
        } else {
            IMAGE_KNIFE_LOG(LOG_ERROR, "File Cache %{public}s Not Finish", cacheName.c_str());
        }
    } else {
        IMAGE_KNIFE_LOG(LOG_ERROR, "File Cache %{public}s Not Found", cacheName.c_str());
    }

    return -1;
}

int64_t FileCacheInterceptorDefault::GetLimitSize(std::string cacheName)
{
    FileCache *fileCache = GetFileCacheByName(cacheName);
    if (fileCache != nullptr) {
        return fileCache->GetAllowMaxDiskLimit();
    }
    return -1;
}

int64_t FileCacheInterceptorDefault::GetLimitNum(std::string cacheName)
{
    FileCache *fileCache = GetFileCacheByName(cacheName);
    if (fileCache != nullptr) {
        return fileCache->GetAllowMaxSizeLimit();
    }
    return -1;
}

void FileCacheInterceptorDefault::SetLimit(size_t maxSize, size_t maxUsage, std::string cacheName)
{
    FileCache *fileCache = GetFileCacheByName(cacheName);
    if (fileCache != nullptr) {
        fileCache->SetCacheLimit(maxSize, maxUsage, !cacheName.empty());
    }
}

void FileCacheInterceptorDefault::PackPixelmapToFile(std::string filePath, OH_PixelmapNative *pixelmap,
                                                     std::string cacheName)
{
    FileCache *fileCache = GetFileCacheByName(cacheName);
    if (fileCache != nullptr) {
        fileCache->PackPixelmapToFile(filePath, pixelmap);
    }
}

void FileCacheInterceptorDefault::EnableExtension(std::string cacheName)
{
    FileCache *fileCache = GetFileCacheByName(cacheName);
    if (fileCache != nullptr) {
        fileCache->EnableExtension();
    }
}

void FileCacheInterceptorDefault::RemoveFile(std::string filePath, std::string cacheName)
{
    FileCache *fileCache = GetFileCacheByName(cacheName);
    if (fileCache != nullptr) {
        fileCache->RemoveFileCache(filePath);
    }
}

void FileCacheInterceptorDefault::RemoveAllFile(std::string cacheName)
{
    FileCache *fileCache = GetFileCacheByName(cacheName);
    if (fileCache != nullptr) {
        fileCache->RemoveAllFileCache();
    }
}

bool FileCacheInterceptorDefault::Resolve(std::shared_ptr<ImageKnifeTask> task)
{
    FileCache *fileCache = &fileCache_;
    auto taskInternal = std::dynamic_pointer_cast<ImageKnifeTaskInternal>(task);
    IMAGE_KNIFE_CHECK_RETURN(taskInternal == nullptr, false);
    auto request = task->GetImageKnifeRequest();
    IMAGE_KNIFE_CHECK_RETURN(request == nullptr, false);
    auto option = request->GetImageKnifeOption();
    IMAGE_KNIFE_CHECK_RETURN(option == nullptr, false);
    auto type = task->GetImageRequestType();
    if (type == ImageRequestType::MAIN_SRC && fileCacheMap_.find(option->fileCacheName) != fileCacheMap_.end()) {
        fileCache = fileCacheMap_[option->fileCacheName];
        IMAGE_KNIFE_LOG(LOG_DEBUG, "%{public}s Use File Cache Small End: %{public}s",
                        taskInternal->GetTaskInfo().c_str(), option->fileCacheName.c_str());
    }

    if (task->cacheTask.type == CacheTaskType::WRITE) {
        return Write(taskInternal, fileCache);
    } else if (task->cacheTask.type == CacheTaskType::READ) {
        return Read(taskInternal, fileCache);
    }

    return false;
}

bool FileCacheInterceptorDefault::Read(std::shared_ptr<ImageKnifeTaskInternal> task, FileCache *fileCache)
{
    FileCache::FileData fileData;
    if (fileCache->GetImageFromDisk(task->cacheTask.cacheKey, fileData)) {
        task->product.imageBuffer = fileData.buffer;
        task->product.imageLength = fileData.size;
        task->product.filePath = fileData.filePath;
        if (fileData.needRename) {
            task->MarkFileCacheNeedRename();
        }
        return true;
    }
    return false;
}

std::string GetFileExtension(uint8_t* buffer, uint32_t length)
{
    return FileTypeUtil::ImageFormatToString(FileTypeUtil::CheckImageFormat(buffer, length));
}

bool FileCacheInterceptorDefault::Write(std::shared_ptr<ImageKnifeTaskInternal> task, FileCache *fileCache)
{
    if (task->product.imageBuffer == nullptr) {
        task->EchoError("Image Buffer Is Null");
        return false;
    }

    if (!fileCache->Contains(task->cacheTask.cacheKey)) {
        // 开启文件后缀的缓存实例，单独进行一次格式判断
        std::string extension;
        if (fileCache->IsEnableExtension()) {
            extension = GetFileExtension(task->product.imageBuffer.get(), task->product.imageLength);
        }
        FileCache::FileData fileData = {.buffer = task->product.imageBuffer,
            .size = task->product.imageLength, .extension = extension};
        fileCache->SaveImageToDisk(task->cacheTask.cacheKey, fileData);
        task->product.filePath = fileData.filePath;
    } else if (task->IsFileCacheNeedRename()) {
        std::string newPath = fileCache->RenameImageFile(task->cacheTask.cacheKey,
            GetFileExtension(task->product.imageBuffer.get(), task->product.imageLength));
        // 重命名失败不改变原有路径
        if (!newPath.empty()) {
            task->product.filePath = newPath;
        }
    }
    return true;
}

}
